package com.shop.rabbit.business;

import cn.common.constant.EntrustConstant;
import cn.model.*;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.shop.rabbit.mapper.BalanceMapper;
import com.shop.rabbit.mapper.EntrustHistoryMapper;
import com.shop.rabbit.mapper.EntrustMapper;
import com.shop.rabbit.rabbitmq.product.TradProducer;
import com.yuan.common.redis.constant.CacheConstants;
import com.yuan.common.redis.lock.RedisDistributedLock;
import com.yuan.common.redis.service.RedisRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.List;
import java.util.Set;

@Slf4j
@Component
public class MatchBusiness {
    @Autowired
    private RedisRepository redisRepository;
    @Autowired
    private TradProducer tradProducer;
    @Autowired
    private EntrustHistoryMapper entrustHistoryMapper;
    @Autowired
    private EntrustMapper entrustMapper;
    @Autowired
    private BalanceMapper balanceMapper;
    public void execute(String message) {
        System.out.println("MatchBusiness 消息送达"+message);
        Entrust entrust = JSONObject.parseObject(message, Entrust.class);//字符串转化为交易单对象
        log.info("entrust 对象 {}",entrust);
        if(entrust.getUld().equals("UP")){
            Set<String> set = null;
            EntrustConstant.Entrust_Type entrustType = null;
            if(entrust.getEntrustType().equals(EntrustConstant.Entrust_Type.BUY)){
                entrustType = EntrustConstant.Entrust_Type.SELL;
                //卖
               Set<String> set_buy = redisRepository.zsetRangByScore(CacheConstants.ENTRUST_ORDER_MATCH_KEY+EntrustConstant.Entrust_Type.BUY+CacheConstants.SPLIT+entrust.getPairsName(),entrust.getPrice().doubleValue(),entrust.getPrice().doubleValue());
                if(!set_buy.isEmpty()){
                    Entrust entrust_buy = JSONObject.parseObject(set_buy.iterator().next(), Entrust.class);
                    log.info("获取盘口交易数据: {}",entrust_buy);
                    entrust.setSort(entrust_buy.getSort()-1);
                }else {
                    entrust.setSort((long) 9999999999f);
                }
                //查询交易卖盘口集合数据范围在0-发起的价格
                set = redisRepository.zsetRangByScore(CacheConstants.ENTRUST_ORDER_MATCH_KEY+EntrustConstant.Entrust_Type.SELL+CacheConstants.SPLIT+entrust.getPairsName(),0,entrust.getPrice().doubleValue());

            }else {
                entrustType = EntrustConstant.Entrust_Type.BUY;
                entrust.setSort(System.currentTimeMillis());
                //查询交易买盘口集合数据
                set = redisRepository.zsetRevRangByScore(CacheConstants.ENTRUST_ORDER_MATCH_KEY+EntrustConstant.Entrust_Type.BUY+CacheConstants.SPLIT+entrust.getPairsName(),entrust.getPrice().doubleValue(),Long.MAX_VALUE);
            }//判断盘口是否有数据
             if(set.isEmpty()){
                 //参数1 key 参数2 交易数据 参数3 排序
                 redisRepository.zsetAdd(CacheConstants.ENTRUST_ORDER_MATCH_KEY+entrust.getEntrustType()+CacheConstants.SPLIT+entrust.getPairsName(),JSONObject.toJSONString(entrust),entrust.getPrice().doubleValue());
             }else {
                 matchOrder(set,entrust,entrustType);
             }
        }
        if (entrust.getUld().equals("DOWN")) {
            this.closeEntrust(entrust);
        }
        //市场
        if (entrust.getUld().equals("MARKET")) {

        }
    }

    //如果是卖单就查询买单集合反之
    public void matchOrder(Set<String> set,Entrust entrust,EntrustConstant.Entrust_Type entrustType) {
        log.info("开始撮合 {} 对象: {}",entrust,entrustType);
        BigDecimal subTotal = entrust.getCount(); //交易数据的数量
        for (String str :set){//循环有序集合里面内容
            if (subTotal.doubleValue() <= 0) {
                break;
            }
            //获取反之的交易数据
           Entrust entrustSetInfo = JSONObject.parseObject(str,Entrust.class);
            BigDecimal count = entrustSetInfo.getCount();//反之数量
            BigDecimal price = entrustSetInfo.getPrice();//反之价格
            int result = count.compareTo(subTotal);
            log.info("发起交易单的数量:{} 价格:{},满足条件:{}",count,price,result);
            //1.反之集合里面交易单数量>判断交易单数量
            if (result > 0) {
                //反之集合里面 把之前的交易数据移除有序集合
               redisRepository.zsetRemove(CacheConstants.ENTRUST_ORDER_MATCH_KEY+entrustType+CacheConstants.SPLIT+entrust.getPairsName(), str);
               entrustSetInfo.setCount(count.subtract(subTotal));//反之交易单还有剩下数量
                //反之集合里面 扣除数量后重新加入有序集合中
               redisRepository.zsetAdd(CacheConstants.ENTRUST_ORDER_MATCH_KEY+entrustType+CacheConstants.SPLIT+entrust.getPairsName(),JSONObject.toJSONString(entrustSetInfo),entrust.getPrice().doubleValue());
               BigDecimal matchPrice = price;//成交价格
               if(entrust.getEntrustType().equals(EntrustConstant.Entrust_Type.SELL)){
                   matchPrice = entrust.getPrice();
               }
               log.info("进来这里没有 {}",str);
                setRedis(entrust.getPairsName(), subTotal, matchPrice, entrust.getEntrustType());//增加交易记录
                matchResult(matchPrice, subTotal, entrust.getPairsName(), entrust.getId(), entrustSetInfo.getId());
                subTotal = new BigDecimal("0");
                break;
            }

            if (result == 0) {
                redisRepository.zsetRemove(CacheConstants.ENTRUST_ORDER_MATCH_KEY + entrustType + CacheConstants.SPLIT
                        + entrust.getPairsName(), str);
                BigDecimal matchPrice = price;
                if (entrust.getEntrustType().equals(EntrustConstant.Entrust_Type.SELL)) {
                    matchPrice = entrust.getPrice();
                }
                setRedis(entrust.getPairsName(), subTotal, matchPrice, entrust.getEntrustType());
                matchResult(matchPrice, subTotal, entrust.getPairsName(), entrust.getId(), entrustSetInfo.getId());
                subTotal = new BigDecimal("0");
                break;
            }
            if (result == -1) {
                subTotal = subTotal.subtract(count);
                redisRepository.zsetRemove(CacheConstants.ENTRUST_ORDER_MATCH_KEY + entrustType + CacheConstants.SPLIT
                        + entrust.getPairsName(), str);
                BigDecimal matchPrice = price;
                if (entrust.getEntrustType().equals(EntrustConstant.Entrust_Type.SELL)) {
                    matchPrice = entrust.getPrice();
                }
                setRedis(entrustSetInfo.getPairsName(), count, matchPrice, entrust.getEntrustType());
                matchResult(matchPrice, count, entrust.getPairsName(), entrust.getId(), entrustSetInfo.getId());
            }
//            if (subTotal.doubleValue() > 0f) {
//                entrust.setCount(subTotal);
//                this.execute(JSONObject.toJSONString(entrust));
//            }
        }
    }

    //参数1 交易对 参数2 交易单数量 参数3 成交价格 参数4 交易类型 作用是记录买卖成交记录
    private void setRedis(String pairsName,BigDecimal subTotal,BigDecimal matchPrice,EntrustConstant.Entrust_Type entrustType) {
            EntrustResult entrustResult = new EntrustResult(pairsName,subTotal,matchPrice,entrustType);
            redisRepository.zsetAdd(CacheConstants.ENTRUST_ORDER_END_KEY+pairsName,JSONObject.toJSONString(entrustResult),System.currentTimeMillis());
    }
    //参数1 成交价格 参数2 交易单数量 参数3 教育对 参数4 交易单id 参数5 反之交易单id 作用 撮合结果进行资产更新，扣除手续费
    private void matchResult(BigDecimal matchPrice,BigDecimal subTotal,String pairsName,String entrustId,String id) {
        EntrustBalance entrustBalance = new EntrustBalance(matchPrice, subTotal, pairsName, entrustId, id);
        //发送消息
        tradProducer.putEntrustBalance(JSONObject.toJSONString(entrustBalance));
    }


    private void closeEntrust(Entrust entrust) {
        List<String> list = redisRepository.keyLikeValue(CacheConstants.ENTRUST_ORDER_MATCH_KEY
                + entrust.getEntrustType() + CacheConstants.SPLIT + entrust.getPairsName(), entrust.getId());
        if (!list.isEmpty()) {
            for (String str : list) {
                redisRepository.zsetRemove(CacheConstants.ENTRUST_ORDER_MATCH_KEY + entrust.getEntrustType()
                        + CacheConstants.SPLIT + entrust.getPairsName(), str);
                entrust.setState(EntrustConstant.Order_State.CLOSE);
                BigDecimal count = entrust.getCount().subtract(entrust.getSurplusCount());
                if (entrust.getMatchPrice() != null) {
                    entrust.setMatchFee(count.multiply(entrust.getMatchPrice()));
                } else {
                    entrust.setMatchFee(new BigDecimal("0"));
                }
                EntrustHistory entrustHistory = new EntrustHistory();
                BeanUtils.copyProperties(entrust, entrustHistory);
                entrustHistoryMapper.insert(entrustHistory);
                entrustMapper.deleteById(entrust.getId());
                closeBalance(entrust);
            }
        }
    }

    private void closeBalance(Entrust entrust) {
        RedisDistributedLock redisDistributedLock = new RedisDistributedLock(redisRepository.getRedisTemplate());
        boolean lock_coin = redisDistributedLock.lock(CacheConstants.MEMBER_BALANCE_COIN_KEY + entrust.getMember(),
                5000, 50, 100);
        if (lock_coin) {
            // 回归用户资产
            QueryWrapper<Balance> wrapperActive = new QueryWrapper<Balance>();
            switch (entrust.getEntrustType().getType()) {
                case "BUY":
                    wrapperActive.eq("currency", entrust.getMainCur());
                    break;
                case "SELL":
                    wrapperActive.eq("currency", entrust.getTokenCur());
                    break;
            }
            wrapperActive.eq("user_id", entrust.getMember());
            Balance balance = balanceMapper.selectOne(wrapperActive);
            BigDecimal blockBalanceActive = new BigDecimal("0");
            if (entrust.getEntrustType().equals(EntrustConstant.Entrust_Type.SELL)) {
                blockBalanceActive = entrust.getSurplusCount();
            } else {
                blockBalanceActive = entrust.getSurplusCount().multiply(entrust.getPrice());
            }
            BigDecimal blockBalance = balance.getBlockedBalance().subtract(blockBalanceActive);
            balance.setBalance(balance.getBalance().add(blockBalanceActive));
            if (blockBalance.compareTo(new BigDecimal("0")) == -1) {
                balance.setBlockedBalance(new BigDecimal("0"));
            } else {
                balance.setBlockedBalance(blockBalance);
            }
            balanceMapper.updateById(balance);
            redisDistributedLock.releaseLock(CacheConstants.MEMBER_BALANCE_COIN_KEY + entrust.getMember());
        } else {
            closeBalance(entrust);
        }

    }
}
